/*
 * Copyright 2000-2020 ALPHA LAB.Inc All Rights Reserved.
 */
package com.alphalab.moonlight.demo.impl.role;
 
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map; 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alphalab.framework.exception.MoRuntimeException;
import com.alphalab.framework.log.IMoLog;
import com.alphalab.framework.log.MoLogFactory;
import com.alphalab.framework.role.GeneralQueryRole;
import com.alphalab.framework.util.MapTransferUtil;
import com.alphalab.moonlight.demo.dao.model.Airline;
import com.alphalab.moonlight.demo.dao.model.AirlineFlight;
import com.alphalab.moonlight.demo.dao.service.IAirlineFlightQueryDao;
import com.alphalab.moonlight.demo.dao.service.IAirlineQueryDao;
import com.alphalab.moonlight.demo.impl.trans.AirlineTransfer;
import com.alphalab.moonlight.demo.impl.trans.PassengerTravelTransfer;
import com.alphalab.moonlight.demo.impl.types.CityTypeEnum;
import com.alphalab.moonlight.demo.impl.types.FlightTypeEnum; 
import com.alphalab.moonlight.demo.service.model.AirlineVO;
import com.alphalab.moonlight.demo.service.model.FlightQueryParamVO;
import com.alphalab.moonlight.demo.service.model.FlightTrendAnalyseVO;
import com.alphalab.moonlight.demo.service.model.FlightTrendQueryParamVO;
import com.alphalab.moonlight.demo.service.model.PassengerTravelVO; 
import com.alphalab.moonlight.demo.service.role.IAirlineQueryRole;

/**
 * AirlineQueryRole.
 * @author allen 
 * @version 1.0.0 2018年2月20日
 */
@Service 
@Transactional
public class AirlineQueryRole extends GeneralQueryRole<AirlineVO, Airline, IAirlineQueryDao, AirlineTransfer> 
	implements IAirlineQueryRole {

	/**
	 * logger.
	 */
	private static final IMoLog logger = MoLogFactory.getLog(AirlineQueryRole.class);
	
	/**
	 * airlineFlightQueryDao.
	 */
	@Autowired
	IAirlineFlightQueryDao airlineFlightQueryDao;
	
	/**
	 * 航班信息表缓存数据.
	 */
	Map<String, AirlineFlight> airlineFlightMap;
	
	/**
	 * 趋势分析父节点汇总数据. key为类别ID value为FlightTrendAnalyseVO
	 */
	Map<Integer, FlightTrendAnalyseVO> pItemMap;
	
	/**
	 * 趋势分析行数据. key为航空公司名 value为FlightTrendAnalyseVO
	 */ 
	Map<String, FlightTrendAnalyseVO> itemMap;	
	

	/**
	 * 初始化时注入airlineQueryDao到AirlineQueryRole中.
	 * @param airlineQueryDao airlineQueryDao
	 */  
	@Autowired
	public void setAirlineQueryDao(final IAirlineQueryDao airlineQueryDao) {
		super.setGeneralQueryDao(airlineQueryDao);
	}
	
	/**
	 * 手动new一个转换器并set到父类中.
	 */
	@Override
	public AirlineTransfer getEntityTransfer() {
		try {
			return super.getEntityTransfer();
		} catch (RuntimeException e) { 
			super.setEntityTransfer(new AirlineTransfer());
		}
		return super.getEntityTransfer();
	}

	/**
	 * 根据航班查询条件对象VO查询航班结果集.
	 * @param flightQueryParamVO 航班查询条件对象VO
	 * @return List<AirlineVO>
	 */
	@Override
	public List<AirlineVO> getAirlineList(final FlightQueryParamVO flightQueryParamVO) { 
		return getEntityTransfer().toVO(getGeneralQueryDao().getAirlineList(flightQueryParamVO, 0, 0));
	}

	/**
	 * 根据航班查询条件对象VO分页查询航班结果集.
	 * @param flightQueryParamVO 航班查询条件对象VO
	 * @param pageIndex 第几页
	 * @param pageSize 每页的记录数
	 * @return List<AirlineVO>
	 */
	@Override
	public List<AirlineVO> getAirlineList(final FlightQueryParamVO flightQueryParamVO, final int pageIndex, 
			final int pageSize) {
		return getEntityTransfer().toVO(getGeneralQueryDao().getAirlineList(flightQueryParamVO, pageIndex, 
				pageSize));
	}

	/**
	 * 根据航班查询条件对象VO查询旅客行程结果集.
	 * @param flightQueryParamVO 航班查询条件对象VO
	 * @return List<PassengerTravelVO>
	 */
	@Override
	public List<PassengerTravelVO> getPassengerTravelList(final FlightQueryParamVO flightQueryParamVO) {
		final PassengerTravelTransfer transfer = new PassengerTravelTransfer();
		return transfer.toVO(getGeneralQueryDao().getPassengerTravelList(flightQueryParamVO));
	}

	/**
	 * 根据航班查询条件对象VO查询航班趋势分析结果集.
	 * @param flightTrendQueryParamVO 航班趋势分析结果VO
	 * @return List<FlightTrendAnalyseVO>
	 */
	@Override
	public List<FlightTrendAnalyseVO> getFlightTrendAnalyseResult(
			final FlightTrendQueryParamVO flightTrendQueryParamVO) throws MoRuntimeException {
		List<FlightTrendAnalyseVO> flightTrendAnalyseVOs = new ArrayList<FlightTrendAnalyseVO>();
		try {
			//获取全部航班信息
			final List<AirlineFlight> airlineFlights = airlineFlightQueryDao.findListWithCondition(null, "", true); 
			//航班信息作为全局变量缓存来使用
			airlineFlightMap = MapTransferUtil.list2MapVO(airlineFlights, "flightId"); 
			/**
			 * 1.查询全部符合条件的航线数据
			 */
			//查询分析城市的航线数据 
			final List<Airline> aCityAirlineList = getAirlineList(flightTrendQueryParamVO, CityTypeEnum.ANALYSE.value());  
			//查询标杆城市的航线数据
			final List<Airline> bCityAirlineList = getAirlineList(flightTrendQueryParamVO, CityTypeEnum.STANDAR.value()); 
			//查询相隔的月份
			final int[] countMonths = getMonthsBetween(flightTrendQueryParamVO.getStartMonth(), 
				flightTrendQueryParamVO.getEndMonth()); 
			//查询全部内地航空公司
			final List<String> rpcCompNames = getAirlineCompNames(airlineFlights, FlightTypeEnum.RPC.value()); 
			//查询全部港澳台航空公司
			final List<String> hmtCompNames = getAirlineCompNames(airlineFlights, FlightTypeEnum.HMT.value()); 
			/**
			 * 2.遍历并统计内地航班数据 
			 */ 
			//初始化map
			pItemMap = new HashMap<>();
			itemMap = new HashMap<>();	  
			//统计分析城市的数据
			fillFlightTrendAnalyseVOMap(rpcCompNames, FlightTypeEnum.RPC.value(), countMonths,
					aCityAirlineList, CityTypeEnum.ANALYSE.value());
			//统计标杆城市的数据
			fillFlightTrendAnalyseVOMap(rpcCompNames, FlightTypeEnum.RPC.value(), countMonths, 
					bCityAirlineList, CityTypeEnum.STANDAR.value()); 		
			//对map进行保存到集合中，需按顺序构造树结构，故父节点先放 
			final FlightTrendAnalyseVO rpcItem = pItemMap.get(FlightTypeEnum.RPC.value()); 
			flightTrendAnalyseVOs.add(rpcItem); 
			//将itemMap数据遍历添加到结果集
			for (Map.Entry<String, FlightTrendAnalyseVO> entry: itemMap.entrySet()) {
				final FlightTrendAnalyseVO itemVO = entry.getValue();
				flightTrendAnalyseVOs.add(itemVO);
			}  				
			/**
			 * 3.遍历并统计港澳台航班数据 
			 */		
			//初始化map，避免最后遍历map到集合时数据重复
			pItemMap = new HashMap<>();
			itemMap = new HashMap<>();		
			//统计分析城市的数据
			fillFlightTrendAnalyseVOMap(hmtCompNames, FlightTypeEnum.HMT.value(), countMonths,
					aCityAirlineList, CityTypeEnum.ANALYSE.value()); 
			//统计标杆城市的数据
			fillFlightTrendAnalyseVOMap(hmtCompNames, FlightTypeEnum.HMT.value(), countMonths, 
					bCityAirlineList, CityTypeEnum.STANDAR.value()); 	
			//对map进行保存到集合中，父节点先放 
			final FlightTrendAnalyseVO hmtItem = pItemMap.get(FlightTypeEnum.HMT.value()); 
			flightTrendAnalyseVOs.add(hmtItem); 
			//将itemMap数据遍历添加到结果集
			for (Map.Entry<String, FlightTrendAnalyseVO> entry: itemMap.entrySet()) {
				final FlightTrendAnalyseVO itemVO = entry.getValue();
				flightTrendAnalyseVOs.add(itemVO);
			}
		
		} catch (Exception ex) {
			//最后捕获意料之外的其他异常，封装为运行时异常并抛出
			logger.error("getFlightTrendAnalyseResult查询异常", ex);
			throw new MoRuntimeException("90001", ex);
		}
		return flightTrendAnalyseVOs;
	}
	
	/**
	 * 根据航班名返回对应的航班分类编号.
	 * @param compName compName
	 * @return int
	 */
	private int getFlightCatalogId(final String compName) {
		//这里不考虑读数据库，直接返回随机数
		final int catalogId = (int) (Math.random() * 900) + 100; 
		return catalogId;
	}
	
	/**
	 * 从结果集中获取航空公司名集合.
	 * @param airlineFlights airlineFlightVOs
	 * @param flightType flightType
	 * @return List<String>
	 */
	private List<String> getAirlineCompNames(final List<AirlineFlight> airlineFlights, final int flightType) {
		final List<String> compNames = new ArrayList<String>();
		for (AirlineFlight airlineFlight : airlineFlights) {
			final String compName = airlineFlight.getFlightCompName();
			if (airlineFlight.getFlightType() == flightType) {
				if (!compNames.contains(compName)) {
					compNames.add(airlineFlight.getFlightCompName());
				} 
			} 
		}
		return compNames;
	}
	
	/**
	 * 获取两个日期间的每个月日期集合.
	 * @param startMonth startMonth
	 * @param endMonth endMonth
	 * @return int[]
	 */
	private int[] getMonthsBetween(final int startMonth, final int endMonth) {
		final int length = endMonth - startMonth + 1;
		final int[] months = new int[length];
		for (int i = 0; i < length; i++) {
			months[i] = startMonth + i;
		}
		return months;
	}
	
	/**
	 * 根据月份、航空公司名从数据集中统计并返回航班数. 
	 * @param cityAirlineList cityAirlineList
	 * @param month month
	 * @param compName compName
	 * @return int
	 */
	private int getCountNum(final List<Airline> cityAirlineList, final int month, final String compName) {
		int result = 0;
		final int thisYear = 2018;
		final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
		for (Airline airline : cityAirlineList) {
			//对应记录的航空公司名直接从map读取
			final String flightName = airlineFlightMap.get(airline.getFlightId()).getFlightCompName();
			final Date beginDate = airline.getBeginDate();
			final String dateStr = sdf.format(beginDate);
			final int yearTemp = Integer.parseInt(dateStr.split("-")[0]);
			final int monthTemp = Integer.parseInt(dateStr.split("-")[1]);
			//判断该日期是否属于该年该月份 
			if (flightName.equals(compName) && yearTemp == thisYear && monthTemp == month) {
				result++;
			} 
		} 
		return result;
	} 
	
	/**
	 * 根据航班查询条件对象VO查询航线结果集.
	 * @param flightTrendQueryParamVO flightTrendQueryParamVO
	 * @param sign 0代表对比城市，1代表标杆城市
	 * @return List<Airline>
	 */
	private List<Airline> getAirlineList(final FlightTrendQueryParamVO flightTrendQueryParamVO, final int sign) { 
		final FlightQueryParamVO queryParamVO = new FlightQueryParamVO();
		queryParamVO.setBeginTime("2018-" + flightTrendQueryParamVO.getStartMonth() + "-01");
		queryParamVO.setEndTime("2018-" + (flightTrendQueryParamVO.getEndMonth() + 1) + "-01"); 
		if (sign == 0) {
			queryParamVO.setDestination(flightTrendQueryParamVO.getAnalyseCity()); 	
		} else if (sign == 1) {
			queryParamVO.setDestination(flightTrendQueryParamVO.getStandarCity()); 	
		} 
		final List<Airline> airlineList = getGeneralQueryDao().getAirlineList(queryParamVO, 0, 0);
		return airlineList;
	}
	
	/**
	 * 遍历数据，填充航班统计数据到集合.
	 * @param compNames 全部航空公司
	 * @param flightType 航班类别
	 * @param countMonths 查询的月份集合
	 * @param cityAirlineList 目标城市的航班数据集合
	 * @param cityType 城市类型 0代表分析城市，1代表标杆城市
	 */
	private void fillFlightTrendAnalyseVOMap(final List<String> compNames, final int flightType,
			final int[] countMonths, final List<Airline> cityAirlineList, final int cityType) { 
		//动态属性前缀名
		final String filedName = cityType == CityTypeEnum.ANALYSE.value() ? "acity_" : "bcity_";
		//父节点航班名
		final String pFlightName = flightType == FlightTypeEnum.RPC.value() ? "内地航班" : "港澳台航班";
		//每个月的各个航空公司数据汇总 key为月份 value为数值
		final Map<Integer, Integer> sumMap = new HashMap<>();
		//父子节点（内地/港澳台）航班的统计数据
		FlightTrendAnalyseVO countItem = null;
		if (pItemMap.keySet().contains(flightType)) {
			countItem = pItemMap.get(flightType);
		} else {
			countItem = new FlightTrendAnalyseVO();		
			countItem.setCatalogId(flightType);
			countItem.setpCatalogId(0); 
			countItem.setFlight(pFlightName);			
		} 
		//遍历每家航空公司
		for (String compName : compNames) { 
			//遍历每个月
			for (int month : countMonths) {  
				//行记录
				FlightTrendAnalyseVO analyseVO = null; 
				if (itemMap.keySet().contains(compName)) {
					analyseVO = itemMap.get(compName);
				} else {
					analyseVO = new FlightTrendAnalyseVO();
					analyseVO.setCatalogId(getFlightCatalogId(compName));
					analyseVO.setpCatalogId(flightType);
					analyseVO.setFlight(compName); 						
				}  
				//获取数值
				final int countNum = getCountNum(cityAirlineList, month, compName);
				//设置值到动态属性中
				analyseVO.set(filedName + month, countNum);
				//更新行数据
				itemMap.put(compName, analyseVO);
				//统计总数据
				if (!sumMap.keySet().contains(month)) {
					sumMap.put(month, countNum);
				} else {
					//更新总数据
					final int sumCount = sumMap.get(month) + countNum; 
					sumMap.put(month, sumCount);
				} 
			} 
		}  
		//更新汇总数据
		for (int key: sumMap.keySet()) {
			final int countNum = sumMap.get(key); 
			countItem.set(filedName + key, countNum);
		}  		 
		pItemMap.put(flightType, countItem);
	}
	
}
